package com.geline.easytrans.handler;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.crypto.SecureUtil;
import com.geline.cloud.core.BaseTimedCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 默认实现类
 *
 * @author: mengxin
 * @date: 2023/9/24 17:23
 */
@Slf4j
public class DefaultTransSQLHandler implements TransSQLHandler {

    private JdbcTemplate jdbcTemplate;
    private BaseTimedCache cache;
    private SqlSessionFactory sqlSessionFactory;

    public DefaultTransSQLHandler(JdbcTemplate jdbcTemplate, SqlSessionFactory sqlSessionFactory, BaseTimedCache cache){
        this.jdbcTemplate = jdbcTemplate;
        this.sqlSessionFactory = sqlSessionFactory;
        this.cache = cache;
    }

    @Override
    public <T> List<T> selectList(String sql, Class<T> mappedClass) {
        String sqlId = String.format("selectList:%s", SecureUtil.md5(sql));
        if(this.cache.containsKey(sqlId)){
            Object object = this.cache.get(sqlId);
            log.debug("get cache sqlId={}, object={}", sqlId, object);
            if(object instanceof List){
                return (List<T>) object;
            }
            return null;
        }

        if(ClassUtil.isSimpleValueType(mappedClass)){
            //原始类型 Integer, Long, String, Date, Number...
            try {
                List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
                //Map转驼峰
                if(mapList!=null && mapList.size()>0){
                    mapList = mapList.stream().map(row -> MapUtil.toCamelCaseMap(row)).collect(Collectors.toList());
                    List list = mapList.stream().map(row -> row.values().iterator().next()).collect(Collectors.toList());
                    this.cache.put(sqlId, list);
                    if(list!=null){
                        return (List<T>) list;
                    }
                }
            }catch (DataAccessException e){
                log.warn("isSimpleValueType={}, queryForList = {}", mappedClass, sql);
                log.warn(e.getMessage());
            }
        }else if(mappedClass==Map.class){
            try {
                List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
                if(mapList!=null && mapList.size()>0){
                    //Map转驼峰
                    mapList = mapList.stream().map(row -> MapUtil.toCamelCaseMap(row)).collect(Collectors.toList());
                    this.cache.put(sqlId, mapList);
                    if(mapList!=null){
                        return (List<T>) mapList;
                    }
                }
            }catch (DataAccessException e){
                log.warn("mappedClass={}, queryForList = {}", mappedClass, sql);
                log.warn(e.getMessage());
            }
        }else {
            try {
                List<T> beanList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(mappedClass));
                this.cache.put(sqlId, beanList);
                if(beanList!=null){
                    return beanList;
                }
            }catch (DataAccessException e){
                log.warn("mappedClass={}, query = {}", mappedClass, sql);
                log.warn(e.getMessage());
            }
        }
        this.cache.put(sqlId, "null");
        return null;
    }

    @Override
    public <T> T selectOne(String sql, Class<T> mappedClass) {
        String sqlId = String.format("selectOne:%s", SecureUtil.md5(sql));
        if(this.cache.containsKey(sqlId)){//如果object=null，containsKey返回为false
            Object object = this.cache.get(sqlId);
            if("null".equals(object)){
                return null;
            }else {
                return (T) object;
            }
        }

        if(mappedClass==Map.class){
            try {
                Map<String, Object> map = jdbcTemplate.queryForMap(sql);
                if(map!=null){
                    this.cache.put(sqlId, map);
                    return (T) map;
                }
            }catch (DataAccessException e){
                if(!e.getMessage().contains("Incorrect result size: expected 1, actual 0")){
                    log.warn("queryForMap = {}", sql);
                    log.warn(e.getMessage());
                }
            }
        }else if(ClassUtil.isSimpleValueType(mappedClass)){//基础类型 Integer, String, Date
            try {
                return jdbcTemplate.queryForObject(sql, mappedClass);
            }catch (DataAccessException e){
                if(!e.getMessage().contains("Incorrect result size: expected 1, actual 0")){
                    log.warn("isSimpleValueType={}, queryForObject = {}", mappedClass, sql);
                    log.warn(e.getMessage());
                }
            }
        }else {//Entity对象
            try {
                T bean = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(mappedClass));
                if(bean!=null){
                    this.cache.put(sqlId, bean);
                    return bean;
                }
            }catch (DataAccessException e){
                if(!e.getMessage().contains("Incorrect result size: expected 1, actual 0")){
                    log.warn("mappedClass={}, queryForObject = {}", mappedClass, sql);
                    log.warn(e.getMessage());
                }
            }
        }
        this.cache.put(sqlId, "null");
        return null;
    }

    @Override
    public String getSql(String sqlIdInMapper) {
        Configuration configuration = sqlSessionFactory.getConfiguration();
        XNode value = configuration.getSqlFragments().get(sqlIdInMapper);
        return value.getStringBody();
    }
}
